import { Ref, type WatchStopHandle, unref, isRef, watch, onUnmounted, onDeactivated, onMounted, onActivated } from "vue";

type TargetRef = EventTarget | Ref<EventTarget | undefined>;
export type UseEventListenerOptions = {
  target?: TargetRef;
  capture?: boolean;
  passive?: boolean;
};

function useEvent<K extends keyof DocumentEventMap>(
  type: K,
  linstener: (event: DocumentEventMap[K]) => void,
  options?: UseEventListenerOptions
): () => void;

function useEvent(
  type: string,
  listener: EventListener,
  options?: UseEventListenerOptions,
): () => void;

function useEvent(
  type: string,
  listener: EventListener,
  options: UseEventListenerOptions = {}
) {
  const { target = window, passive = false, capture = false } = options;

  let cleaned = false;
  let attached = false;

  const add = (target?: TargetRef) => {
    if (cleaned) {
      return;
    }
    const element = unref(target);
    if (element && !attached) {
      element.addEventListener(
        type,
        listener,
        { capture, passive }
      );
      attached = true;
    }
  };

  const remove = (target?: TargetRef) => {
    if (cleaned) {
      return;
    }
    const element = unref(target)
    if (element && attached) {
      element.removeEventListener(
        type,
        listener,
        capture
      );
      attached = false;
    }
  };

  onMounted(() => add(target));
  onUnmounted(() => remove(target));
  onActivated(() => add(target));
  onDeactivated(() => remove(target));

  let stopWath: WatchStopHandle = () => { };
  if (isRef(target)) {
    stopWath = watch(target, (newVal, oldVal) => {
      remove(oldVal);
      add(newVal);
    });
  }

  return () => {
    stopWath();
    remove(target);
    cleaned = true;
  }
}

export default useEvent;